package com.atguigu.tingshu.common.login;

import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import jakarta.servlet.http.HttpServletRequest;
import lombok.val;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
public class GuiguLoginAspect {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private HttpServletRequest httpServletRequest;

    //    在* com.atguigu.tingshu.*.api.*.*(..))的方法下，和标注了tsLogin注解的方法下插入该切面类
    @Around("@annotation(guiguLogin)") //标注本切面类切入带有guiguLogin的方法，以及切入的类是啥
    public Object login(ProceedingJoinPoint joinPoint,GuiguLogin guiguLogin) throws Throwable {
        try {
//        1.本切面类用于判断这个用户是否登录，一处登录处处使用
//        2.要利用微信小程序登录接口，通过前端传递的code码，调用api能够获取openid（每个微信唯一的）
//        3.登陆成功，将用户信息保存到redis，没有注册步骤，生成一个token返回前端，并将该token保存到请求头
//        4.重点判断请求头中是否有token即可，前端还会将token存储到浏览器本地缓存中
//        5.“能拿到 token”≠“已登录且有权限”，tsLogin.required() 就是保证token是有效的token
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes attributes =  (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = attributes.getRequest();

//        从中获取到token数据：
            String token = request.getHeader("token");
//        编写用户在访问需要登录的业务时，没有登录，则跳转到登录业务逻辑
            if (guiguLogin.required()){
    //            必须要登录，如果没登录，则跳转
                if (StringUtils.isEmpty(token)){
                    throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
                }
    //            token不为空，说明登陆过，将这个用户信息保存到redis（开始查缓存中是否有用户信息，
    //            没有也不可以登录）缓存的key=token；value=userInfo
                String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX+token;
    //            获取缓存中用户数据：
                UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(loginKey);
                if (null == userInfo) {
                    throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
                }
            }
//        走到这里，说明不需要tsLogin，但只要token不为空，就将用户id存到本地线程中，便于后台微服务获取用户id
            if (!StringUtils.isEmpty(token)){
    //            组成redis的缓存key：
                String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX+token;
    //            获取redis缓存中该用户数据
                UserInfo userInfo = (UserInfo) this.redisTemplate.opsForValue().get(loginKey);
                if (null != userInfo){ //如果redis中存了该用户信息
    //                存储用户id
                    AuthContextHolder.setUserId(userInfo.getId()); //在当前线程中临时存储和获取用户id，用于实现后续方法
                }
            }
            return joinPoint.proceed(); //切面方法结束，开始执行方法体
        } finally {
            AuthContextHolder.removeUserId();//防止内存泄漏！
//            这样，以后在添加了@TsLogin注解的地方，就会提示我们需要登录！
        }
    }
}
